package task.snake.model;

import java.awt.Point;
import java.util.LinkedList;

/**
 * Class Snake extends Creature class and describes snake entity.
 * 
 * @author Nadezhda Makarkina
 * 
 */
public class Snake extends Creature {
	/*
	 * indicates if the snake has eaten a frog and has to grow during current
	 * movement
	 */
	private Eat eat = Eat.NOT;
	private Direction direction = Direction.FORWARD;
	private int length;

	/**
	 * Initializes a newly created Snake object.
	 * 
	 * @param p
	 *            position of head
	 * @param sm
	 *            snake model object
	 * @param length
	 *            of snake
	 */
	public Snake(Point p, SnakeModel sm, int length) {
		super(p, sm, sm.getSleepTime());
		for (int i = 1; i < length; i++) {
			body.add(new Point(p.x, p.y - i));
		}
		this.length = length;
	}

	private enum Direction {
		FORWARD, LEFT, RIGHT
	}

	// think about access
	enum Eat {
		NOT, GREEN_FROG, RED_FROG
	}

	/**
	 * Returns direction of movement.
	 * 
	 * @return direction
	 */
	public Direction getDirection() {
		return direction;
	}

	/**
	 * Sets the eat indicator.
	 */
	public void setEat(Eat e) {
		eat = e;
	}

	/**
	 * Returns eat indicator which means, that snake has to grow during current
	 * movement.
	 * 
	 * @return eat
	 */
	public Eat isEat() {
		return eat;
	}

	/**
	 * Returns list of points, which describes location of the snake on the play
	 * field.
	 * 
	 * @return
	 */
	public LinkedList<Point> getLocation() {
		return body;
	}

	/**
	 * Describes movement of snake on the play field
	 */
	@Override
	protected void move() {
		switch (direction) {
		case LEFT:
			moveLeft();
			break;
		case RIGHT:
			moveRight();
			break;
		case FORWARD:
			moveForward();
			break;
		}
		// check if the snake clash a wall or itself
		if (onClash(sm.getSnake())) {
			return;
		}
		// check if the snake eat a frog
		for (int i = 0; i < sm.getFrogs().size(); i++) {
			sm.getFrogs().get(i).onClash(this);
		}
		// if snake didn't eat a frog then cut it's tail
		if (eat == Eat.NOT) {
			// after movement setup old sell free
			sm.setCell(body.removeLast(), null, false);
		} 
		if (eat == Eat.RED_FROG) {
			sm.setCell(body.removeLast(), null, false);
			if (body.size() > length) {
				sm.setCell(body.removeLast(), null, false);
			}
		}
		eat = Eat.NOT;
		/*
		 * if (onClash(sm.getSnake())){ return; }
		 */

		// and new cell busy
		sm.setCell(getHeadLocation(), this, true);
		direction = Direction.FORWARD;
	}

	/*
	 * Method describes movement to forward.
	 */
	private void moveForward() {
		// vertical movement
		if (body.get(0).x == body.get(1).x) {
			if (body.get(0).y > body.get(1).y) {
				body.addFirst(new Point(body.get(0).x, body.get(0).y + 1));
			} else {
				body.addFirst(new Point(body.get(0).x, body.get(0).y - 1));
			}
		} else {// then (body.get(0).y == body.get(1).y) horizontal movement
			if (body.get(0).x > body.get(1).x) {
				body.addFirst(new Point(body.get(0).x + 1, body.get(0).y));
			} else {
				body.addFirst(new Point(body.get(0).x - 1, body.get(0).y));
			}
		}
	}

	/*
	 * Method describes movement to left.
	 */
	private void moveLeft() {
		// vertical movement
		if (body.get(0).x == body.get(1).x) {
			if (body.get(0).y > body.get(1).y) {
				body.addFirst(new Point(body.get(0).x + 1, body.get(0).y));
			} else {
				body.addFirst(new Point(body.get(0).x - 1, body.get(0).y));
			}
		} else {// then (body.get(0).y == body.get(1).y) horizontal movement
			if (body.get(0).x > body.get(1).x) {
				body.addFirst(new Point(body.get(0).x, body.get(0).y - 1));
			} else {
				body.addFirst(new Point(body.get(0).x, body.get(0).y + 1));
			}
		}
	}

	/*
	 * Method describes movement to right.
	 */
	private void moveRight() {
		// vertical movement
		if (body.get(0).x == body.get(1).x) {
			if (body.get(0).y > body.get(1).y) {
				body.addFirst(new Point(body.get(0).x - 1, body.get(0).y));
			} else {
				body.addFirst(new Point(body.get(0).x + 1, body.get(0).y));
			}
		} else {// then (body.get(0).y == body.get(1).y) horizontal movement
			if (body.get(0).x > body.get(1).x) {
				body.addFirst(new Point(body.get(0).x, body.get(0).y + 1));
			} else {
				body.addFirst(new Point(body.get(0).x, body.get(0).y - 1));
			}
		}
	}

	/**
	 * Sets direction of movement to the left.
	 */
	public synchronized void turnLeft() {
		direction = Direction.LEFT;
	}

	/**
	 * Sets direction of movement to the right.
	 */
	public synchronized void turnRight() {
		direction = Direction.RIGHT;
	}

	/**
	 * Describe situation when the snake clash itself or the wall.
	 */
	@Override
	protected boolean onClash(Snake snake) {
		// first check if the snake eats itself
		for (int i = 1; i < snake.body.size(); i++) {
			if (snake.getHeadLocation().equals(this.body.get(i))) {
				sm.stopCreatures();
				body.removeFirst();
				sm.setCell(new Point(0,0), null, false);
				return true;
			}
		}
		// then check the snake clashes the wall
		if (body.get(0).x > sm.getWidth() - 1 | body.get(0).x < 0
				| body.get(0).y > sm.getHeight() - 1 | body.get(0).y < 0) {
			sm.stopCreatures();
			body.removeFirst();
			sm.setCell(new Point(0,0), null, false);
			return true;
		}
		return false;
	}
}
